package com.tc.shiro.session;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class RedisSessionDao extends AbstractSessionDAO {

    private static final String MERC_PORTAL_SHIRO_SESSION = "merchant_portal:web:session:";
    private static Logger log = LoggerFactory.getLogger(RedisSessionDao.class);
    @Autowired
    private RedisTemplate redisTemplate;
    //@Autowired
    //private PropertyManager propertyManager;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session != null) {
            Serializable sessionId = session.getId();
            Serializable ridesKey = getRedisSessionKey(sessionId);
            // 超时时间
            long timeout = Long.parseLong("1800000");
            redisTemplate.boundValueOps(ridesKey).set(session, timeout, TimeUnit.MILLISECONDS);
        }
    }

    @Override
    public void delete(Session session) {
        if (session == null) {
            return;
        }
        Serializable sessionId = session.getId();
        if (sessionId != null) {
            Serializable ridesKey = getRedisSessionKey(sessionId);
            redisTemplate.delete(ridesKey);
        }
    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<Serializable> keys = redisTemplate.keys(MERC_PORTAL_SHIRO_SESSION + "*");
        Set<Session> sessions = new HashSet<>();
        if (keys != null && !keys.isEmpty()) {
            Iterator it = keys.iterator();
            while (it.hasNext()) {
                Serializable ridesKey = (Serializable) it.next();
                Session session = (Session) redisTemplate.boundValueOps(ridesKey).get();
                if (session != null) {
                    sessions.add(session);
                }
            }
        }
        return sessions;
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        Serializable ridesKey = getRedisSessionKey(sessionId);
        // 超时时间
        long timeout = Long.parseLong("1800000");
        redisTemplate.boundValueOps(ridesKey).set(session, timeout, TimeUnit.MILLISECONDS);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (null == sessionId) {
            return null;
        }
        Serializable ridesKey = getRedisSessionKey(sessionId);
        Object obj = redisTemplate.boundValueOps(ridesKey).get();
        if (obj != null) {
            return (Session) obj;
        }
        return null;
    }

    public Session getSessionById(Serializable sessionId) {
        return doReadSession(sessionId);
    }

    /**
     * 获取redis中的session key
     * @param sessionId
     * @return
     */
    private String getRedisSessionKey(Serializable sessionId) {
        return this.MERC_PORTAL_SHIRO_SESSION + sessionId;
    }

}
